import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:intl/intl.dart';
import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';
import 'package:smooth_app/helpers/launch_url_helper.dart';
import 'package:smooth_app/l10n/app_localizations.dart';
import 'package:smooth_app/pages/prices/infinite_scroll_list.dart';
import 'package:smooth_app/pages/prices/infinite_scroll_manager.dart';
import 'package:smooth_app/pages/prices/price_data_entry.dart';
import 'package:smooth_app/pages/prices/price_image_container.dart';
import 'package:smooth_app/pages/prices/price_proof_page.dart';
import 'package:smooth_app/query/product_query.dart';
import 'package:smooth_app/resources/app_icons.dart' as icons;
import 'package:smooth_app/themes/smooth_theme.dart';
import 'package:smooth_app/themes/smooth_theme_colors.dart';
import 'package:smooth_app/themes/theme_provider.dart';
import 'package:smooth_app/widgets/smooth_app_bar.dart';
import 'package:smooth_app/widgets/smooth_scaffold.dart';
import 'package:vector_graphics/vector_graphics.dart';

/// Page that displays the latest proofs of the current user.
class PricesProofsPage extends StatefulWidget {
  const PricesProofsPage({required this.selectProof});

  /// Do we want to select a proof (true), or just to see its details (false)?
  final bool selectProof;

  @override
  State<PricesProofsPage> createState() => _PricesProofsPageState();
}

class _PricesProofsPageState extends State<PricesProofsPage>
    with TraceableClientMixin {
  late final _InfiniteScrollProofManager _proofManager =
      _InfiniteScrollProofManager(selectProof: widget.selectProof);

  @override
  Widget build(BuildContext context) {
    final AppLocalizations appLocalizations = AppLocalizations.of(context);
    final SmoothColorsThemeExtension extension = context
        .extension<SmoothColorsThemeExtension>();
    final bool lightTheme = context.lightTheme();

    return SmoothScaffold(
      backgroundColor: lightTheme ? extension.primaryLight : null,
      appBar: SmoothAppBar(
        centerTitle: false,
        leading: const SmoothBackButton(),
        title: Text(appLocalizations.user_search_proofs_title),
        actions: <Widget>[
          IconButton(
            tooltip: appLocalizations.prices_app_button,
            icon: const Icon(Icons.open_in_new),
            onPressed: () async => LaunchUrlHelper.launchURL(
              OpenPricesAPIClient.getUri(
                path: 'dashboard/proofs',
                uriHelper: ProductQuery.uriPricesHelper,
              ).toString(),
            ),
          ),
        ],
      ),
      body: InfiniteScrollList<Proof>(manager: _proofManager),
    );
  }
}

/// A manager for handling proof data with infinite scrolling
class _InfiniteScrollProofManager extends InfiniteScrollManager<Proof> {
  _InfiniteScrollProofManager({required this.selectProof});

  static const int _pageSize = 10;
  final bool selectProof;
  String? _bearerToken;

  @override
  Future<void> fetchInit(final BuildContext context) async {
    if (_bearerToken != null) {
      return;
    }

    final User user = ProductQuery.getWriteUser();
    final MaybeError<String> token = await ProductQuery.getPriceToken(
      user,
      context.read<LocalDatabase>(),
    );

    if (token.isError) {
      throw Exception(token.error ?? 'Could not authenticate with the server');
    }

    _bearerToken = token.value;
  }

  @override
  Future<void> fetchData(final int pageNumber) async {
    final User user = ProductQuery.getWriteUser();
    final MaybeError<GetProofsResult> result =
        await OpenPricesAPIClient.getProofs(
          GetProofsParameters()
            ..orderBy = <OrderBy<GetProofsOrderField>>[
              const OrderBy<GetProofsOrderField>(
                field: GetProofsOrderField.created,
                ascending: false,
              ),
            ]
            ..owner = user.userId
            ..pageSize = _pageSize
            ..pageNumber = pageNumber,
          uriHelper: ProductQuery.uriPricesHelper,
          bearerToken: _bearerToken!,
        );

    if (result.isError) {
      throw Exception(result.error ?? 'Failed to fetch proofs');
    }

    final GetProofsResult value = result.value;
    updateItems(
      newItems: value.items,
      pageNumber: value.pageNumber,
      totalItems: value.total,
      totalPages: value.numberOfPages,
    );
  }

  @override
  Widget buildItem({required BuildContext context, required Proof item}) {
    if (item.filePath == null) {
      return EMPTY_WIDGET;
    }

    final SmoothColorsThemeExtension extension = context
        .extension<SmoothColorsThemeExtension>();
    final bool lightTheme = context.lightTheme();

    return SmoothCard(
      elevation: 5.0,
      elevationColor: Colors.black26,
      margin: const EdgeInsetsDirectional.only(
        top: MEDIUM_SPACE,
        start: 8.0,
        end: 8.0,
      ),
      padding: EdgeInsets.zero,
      color: lightTheme ? null : extension.primaryUltraBlack,
      child: InkWell(
        borderRadius: ROUNDED_BORDER_RADIUS,
        onTap: () async {
          if (selectProof) {
            Navigator.of(context).pop(item);
            return;
          }
          return Navigator.push<void>(
            context,
            MaterialPageRoute<void>(
              builder: (BuildContext context) => PriceProofPage(item),
            ),
          );
        },
        child: _PriceProofListItem(item),
      ),
    );
  }

  @override
  String formattedItemCount(
    BuildContext context,
    int loadedItems,
    int? totalItems,
  ) {
    final AppLocalizations appLocalizations = AppLocalizations.of(context);
    return totalItems != null
        ? appLocalizations.proofs_count_with_total(loadedItems, totalItems)
        : appLocalizations.proof_count(loadedItems);
  }

  @override
  Widget get emptyListIcon => const SvgPicture(
    AssetBytesLoader('assets/icons/price_receipt_empty.svg.vec'),
  );

  @override
  String emptyListTitle(AppLocalizations appLocalizations) =>
      appLocalizations.prices_proof_empty_title;

  @override
  String emptyListExplanation(AppLocalizations appLocalizations) =>
      appLocalizations.prices_proof_empty_explanation;
}

class _PriceProofListItem extends StatelessWidget {
  const _PriceProofListItem(this.proof);

  final Proof proof;

  @override
  Widget build(BuildContext context) {
    final AppLocalizations appLocalizations = AppLocalizations.of(context);
    final SmoothColorsThemeExtension extension = context
        .extension<SmoothColorsThemeExtension>();
    final bool lightTheme = context.lightTheme();
    final String locale = Localizations.localeOf(context).toLanguageTag();

    return IconTheme.merge(
      data: IconThemeData(
        color: lightTheme ? extension.primaryNormal : extension.primaryMedium,
      ),
      child: Padding(
        padding: const EdgeInsetsDirectional.all(SMALL_SPACE),
        child: Row(
          spacing: MEDIUM_SPACE,
          children: <Widget>[
            PriceImageContainer(
              size: Size.square(MediaQuery.widthOf(context) * 0.3),
              borderRadius: BorderRadius.all(
                Radius.circular(ROUNDED_RADIUS.x - SMALL_SPACE),
              ),
              imageProvider: NetworkImage(
                proof
                    .getFileUrl(
                      uriProductHelper: ProductQuery.uriPricesHelper,
                      isThumbnail: true,
                    )
                    .toString(),
              ),
              count: proof.priceCount,
            ),
            Expanded(
              child: Column(
                spacing: SMALL_SPACE,
                children: <Widget>[
                  PriceDataEntry(
                    icon: const icons.Clock.alt(size: 19.0),
                    label: DateFormat.yMd(
                      locale,
                    ).format(proof.date ?? proof.created),
                  ),
                  PriceDataEntry(
                    icon: const icons.Shop(size: 20.0),
                    label:
                        proof.location?.name ??
                        appLocalizations.prices_entry_shop_not_found,
                    labelPadding: const EdgeInsetsDirectional.only(bottom: 2.5),
                  ),
                  PriceDataEntry(
                    icon: const icons.Location(size: 19.44),
                    label:
                        '${proof.location?.city}, ${proof.location?.country ?? ''}',
                    labelPadding: const EdgeInsetsDirectional.only(bottom: 2.5),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
